package cn.keniykey.mc.landterritory.land.api;

import java.lang.reflect.Type;
import java.text.DecimalFormat;

import javax.annotation.Nullable;

import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

public final class LandPosition implements Cloneable{
	private transient static Gson gson;
	private static class LandPositionJsonGosn implements JsonSerializer<LandPosition>,JsonDeserializer<LandPosition>{
		@Override
		public LandPosition deserialize(JsonElement jsonen, Type type, JsonDeserializationContext josnd)
				throws JsonParseException {
			final JsonObject jobj = jsonen.getAsJsonObject();
			final String world = jobj.get("world").getAsString();
			final double x = jobj.get("x").getAsDouble();
			final double y = jobj.get("y").getAsDouble();
			final double z = jobj.get("z").getAsDouble();
			final float yaw = jobj.get("yaw").getAsFloat();
			final float pitch = jobj.get("pitch").getAsFloat();
			return new LandPosition(world, x, y, z, yaw, pitch);
		}
		@Override
		public JsonElement serialize(LandPosition pos, Type type, JsonSerializationContext josns) {
			final JsonObject jobj = new JsonObject();
			jobj.addProperty("world", (String)pos.world);
			jobj.addProperty("x", (double)pos.x);
			jobj.addProperty("y", (double)pos.y);
			jobj.addProperty("z", (double)pos.z);
			jobj.addProperty("yaw", (float)pos.yaw);
			jobj.addProperty("pitch", (float)pos.pitch);
			return jobj;
		}
	}
	
	static {
		gson = new GsonBuilder().registerTypeAdapter(LandPosition.class, new LandPositionJsonGosn()).create();
	}
	
	private static final DecimalFormat df = new DecimalFormat("#.00");
	private String world;
	private double x,y,z;
	private float yaw,pitch;
	
	public LandPosition(String world,double x,double y,double z,float yaw,float pitch) {
		this.world = world; this.yaw = yaw; this.pitch = pitch;
		this.x = x; this.y = y; this.z = z;
	}
	public LandPosition(Location loc) {
		this(loc.getWorld().getName(),Double.valueOf(df.format(loc.getX())),Double.valueOf(df.format(loc.getY())),Double.valueOf(df.format(loc.getZ())),
				Float.valueOf(df.format(loc.getYaw())), Float.valueOf(df.format(loc.getPitch())));
	}
	
	@Override
	public LandPosition clone(){
		return new LandPosition (this.world, this.x, this.y, this.z, this.yaw, this.pitch);
	}
	@Override
	public int hashCode() {
		return (this.world.hashCode() + (int)(this.x - this.y + this.z) - (int)(this.yaw - this.pitch));
	}
	@Override
    public boolean equals(Object obj) {
		if(obj == null) return false;
		if (obj == this) return true;
		if (!(obj instanceof LandPosition)) return false;
		final LandPosition pos = (LandPosition) obj;
		return (this.world.equals(pos.world) && this.x == pos.x && this.y == pos.y && this.z == pos.z && this.yaw == pos.yaw && this.pitch == pos.pitch);
    }
	@Override
	public String toString() {
		return "[world:" + world + " x:" + x + " y:" + y + " z:" + z + " yaw:" + yaw + " pitch:" + pitch + "]";
	}
	
	public String toJson() {
		return gson.toJson(this);
	}
	public static LandPosition FromJson(String json) {
		return gson.fromJson(json, LandPosition.class);
	}
	
	public String getWorldName() {
		return this.world;
	}
	public double getX() {
		return this.x;
	}
	public double getY() {
		return this.y;
	}
	public double getZ() {
		return this.z;
	}
	public float getYaw() {
		return this.yaw;
	}
	public float getPitch() {
		return this.pitch;
	}
	@Nullable
	public Location toLocation() {
		final World bworld = Bukkit.getServer().getWorld(this.world);
		return world == null ? null : new Location(bworld,this.x,this.y,this.z,this.yaw,this.pitch);
	}
	public void updateFromLocation(Location loc) {
		this.world = loc.getWorld().getName();
		this.x = Double.valueOf(df.format(loc.getX()));
		this.y = Double.valueOf(df.format(loc.getY()));
		this.z = Double.valueOf(df.format(loc.getZ()));
		this.yaw = loc.getYaw();
		this.pitch = loc.getPitch();
	}
}